package com.ruoyi.course.UtilMCTSCourseScheduling;

import com.ruoyi.classroom.domain.classroom;
import com.ruoyi.classroom.service.IclassroomService;
import com.ruoyi.course.UtilMCTSCourseScheduling.DataPreprocessor.CourseClassInfo;
import com.ruoyi.course.UtilMCTSCourseScheduling.DataPreprocessor.SchedulingData;
import com.ruoyi.course.UtilMCTSCourseScheduling.DataPreprocessor.TimeSlot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.DayOfWeek;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 简单排课器 - 生成一套可行的排课方案
 * 采用贪婪策略，不追求最优解但确保满足基本约束
 */
@Component
public class SimpleScheduler {
    private static final Logger logger = LoggerFactory.getLogger(SimpleScheduler.class);

    @Autowired
    private DataPreprocessor dataPreprocessor;

    @Autowired
    private IclassroomService classroomService;

    // 排课结果：课程班ID -> 分配的时间槽列表
    private Map<String, List<TimeSlot>> schedulingResult = new HashMap<>();

    // 已占用的教师时间：教师ID -> 时间槽集合
    private Map<String, Set<TimeSlot>> occupiedTeacherTime = new HashMap<>();

    // 已占用的教室时间：教室ID -> 时间槽集合
    private Map<String, Set<TimeSlot>> occupiedClassroomTime = new HashMap<>();

    // 已占用的学生时间：学生ID -> 时间槽集合
    private Map<String, Set<TimeSlot>> occupiedStudentTime = new HashMap<>();

    /**
     * 执行简单排课，生成一套可行方案
     * @param semesterId 学期ID
     * @param academicYear 学年
     * @return 排课结果
     */
    public Map<String, List<TimeSlot>> generateSimpleSchedule(int semesterId, int academicYear) {
        // 1. 重置状态
        reset();

        // 2. 获取预处理数据
        SchedulingData schedulingData = dataPreprocessor.processSchedulingData(semesterId, academicYear);
        if (schedulingData.hasCriticalErrors()) {
            logger.error("数据存在严重错误，无法进行排课");
            return Collections.emptyMap();
        }

        // 3. 初始化已占用时间（从已有占用记录）
        initOccupiedTimes(schedulingData);

        // 4. 获取所有课程班并按优先级排序（高优先级先排）
        List<CourseClassInfo> sortedCourses = new ArrayList<>(schedulingData.getCourseClasses().values());
        sortedCourses.sort((c1, c2) -> Integer.compare(c2.getPriority(), c1.getPriority()));

        logger.info("开始简单排课，共 {} 个课程班", sortedCourses.size());

        // 5. 逐个处理课程班
        for (CourseClassInfo course : sortedCourses) {
            logger.info("处理课程班: {}，每周 {} 次，每次 {} 节",
                    course.getId(), course.getLessonCount(), course.getWeekType());

            // 为课程分配所需的时间段
            List<TimeSlot> assignedSlots = assignTimeSlotsForCourse(course, schedulingData);

            if (assignedSlots.size() < course.getLessonCount()) {
                logger.warn("课程班 {} 未能分配足够的时间段，需要 {} 次，实际分配 {} 次",
                        course.getId(), course.getLessonCount(), assignedSlots.size());
            }

            schedulingResult.put(course.getId(), assignedSlots);
        }

        logger.info("简单排课完成，成功为 {} 个课程班分配时间", schedulingResult.size());
        return new HashMap<>(schedulingResult);
    }

    /**
     * 为单个课程班分配时间槽
     */
    private List<TimeSlot> assignTimeSlotsForCourse(CourseClassInfo course, SchedulingData data) {
        List<TimeSlot> assignedSlots = new ArrayList<>();
        String teacherId = course.getPreferredTeacherId();

        // 获取该课程班的学生列表
        Map<String, List<String>> studentMap = dataPreprocessor.getStudentIdsByCourseClassId();
        List<String> students = studentMap.getOrDefault(course.getId(), Collections.emptyList());

        // 需要为每周安排的次数
        int requiredWeeklySessions = course.getLessonCount();
        // 每次课需要的连续节数
        int continuousSessions = course.getWeekType();

        // 获取可用时间槽并按星期和节次排序
        List<TimeSlot> availableSlots = new ArrayList<>(data.getAllAvailableTimeSlots());
        availableSlots.sort((s1, s2) -> {
            if (!s1.getDayOfWeek().equals(s2.getDayOfWeek())) {
                return s1.getDayOfWeek().compareTo(s2.getDayOfWeek());
            }
            return Integer.compare(s1.getStartSession(), s2.getStartSession());
        });

        // 尝试找到符合条件的时间段
        Set<DayOfWeek> usedDays = new HashSet<>();
        int assignedCount = 0;

        for (TimeSlot slot : availableSlots) {
            // 如果已经分配了足够的次数，停止寻找
            if (assignedCount >= requiredWeeklySessions) {
                break;
            }

            // 检查是否需要连续节次
            TimeSlot actualSlot = slot;
            if (continuousSessions > 1) {
                int endSession = slot.getStartSession() + continuousSessions - 1;
                // 检查连续节次是否有效且不跨时段
                if (endSession > 10) {
                    continue;
                }
                actualSlot = new TimeSlot(slot.getDayOfWeek(), slot.getStartSession(), endSession);
                if (actualSlot.isCrossPeriod()) {
                    continue; // 跳过跨时段的连续节次
                }
            }

            // 检查该天是否已经安排过该课程（避免同一天安排多次）
            if (usedDays.contains(actualSlot.getDayOfWeek())) {
                continue;
            }

            // 检查教师时间冲突
            if (hasTeacherConflict(teacherId, actualSlot)) {
                continue;
            }

            // 检查学生时间冲突
            if (hasStudentConflict(students, actualSlot)) {
                continue;
            }

            // 查找可用教室
            String availableClassroom = findAvailableClassroom(course, actualSlot, data);
            if (availableClassroom == null) {
                continue;
            }

            // 分配该时间段
            assignedSlots.add(actualSlot);
            usedDays.add(actualSlot.getDayOfWeek());
            assignedCount++;

            // 记录占用情况
            markTeacherAsOccupied(teacherId, actualSlot);
            markClassroomAsOccupied(availableClassroom, actualSlot);
            markStudentsAsOccupied(students, actualSlot);

            logger.info("课程班 {} 分配时间: {}，教室: {}",
                    course.getId(), actualSlot, availableClassroom);
        }

        return assignedSlots;
    }

    /**
     * 查找可用教室
     */
    private String findAvailableClassroom(CourseClassInfo course, TimeSlot slot, SchedulingData data) {
        // 查询符合条件的教室（类型匹配、容量足够）
        classroom query = new classroom();
        // 修复：使用正确的方法名设置教室类型（假设正确方法是setRoomTypeId）
        query.setClassroomType(course.getRoomType());
        // 修复：将int转换为Long
        query.setCampus((long) course.getCampus());
        List<classroom> suitableClassrooms = classroomService.selectclassroomList(query);

        // 过滤掉容量不足的教室
        suitableClassrooms = suitableClassrooms.stream()
                .filter(room -> room.getCapacity() >= course.getStudentCount())
                .collect(Collectors.toList());

        // 检查每个教室是否可用
        for (classroom room : suitableClassrooms) {
            String classroomId = room.getId();
            if (!hasClassroomConflict(classroomId, slot)) {
                return classroomId;
            }
        }

        // 如果没有找到完全匹配的，尝试容量足够的其他类型教室
        if (suitableClassrooms.isEmpty()) {
            classroom capacityQuery = new classroom();
            // 修复：将int转换为Long
            capacityQuery.setCapacity((long) course.getStudentCount());
            // 修复：将int转换为Long
            capacityQuery.setCampus((long) course.getCampus());
            suitableClassrooms = classroomService.selectclassroomList(capacityQuery);

            for (classroom room : suitableClassrooms) {
                String classroomId = room.getId();
                if (!hasClassroomConflict(classroomId, slot)) {
                    // 修复：使用正确的方法名获取教室类型（假设正确方法是getRoomTypeId）
                    logger.warn("课程班 {} 分配了非首选类型的教室 {}，类型: {}",
                            course.getId(), classroomId, room.getClassroomType());
                    return classroomId;
                }
            }
        }

        return null;
    }

    /**
     * 检查教师时间冲突
     */
    private boolean hasTeacherConflict(String teacherId, TimeSlot slot) {
        Set<TimeSlot> occupied = occupiedTeacherTime.getOrDefault(teacherId, new HashSet<>());
        return occupied.stream().anyMatch(occupiedSlot -> occupiedSlot.overlaps(slot));
    }

    /**
     * 检查教室时间冲突
     */
    private boolean hasClassroomConflict(String classroomId, TimeSlot slot) {
        Set<TimeSlot> occupied = occupiedClassroomTime.getOrDefault(classroomId, new HashSet<>());
        return occupied.stream().anyMatch(occupiedSlot -> occupiedSlot.overlaps(slot));
    }

    /**
     * 检查学生时间冲突
     */
    private boolean hasStudentConflict(List<String> students, TimeSlot slot) {
        for (String studentId : students) {
            Set<TimeSlot> occupied = occupiedStudentTime.getOrDefault(studentId, new HashSet<>());
            if (occupied.stream().anyMatch(occupiedSlot -> occupiedSlot.overlaps(slot))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 标记教师已占用时间
     */
    private void markTeacherAsOccupied(String teacherId, TimeSlot slot) {
        occupiedTeacherTime.computeIfAbsent(teacherId, k -> new HashSet<>()).add(slot);
    }

    /**
     * 标记教室已占用时间
     */
    private void markClassroomAsOccupied(String classroomId, TimeSlot slot) {
        occupiedClassroomTime.computeIfAbsent(classroomId, k -> new HashSet<>()).add(slot);
    }

    /**
     * 标记学生已占用时间
     */
    private void markStudentsAsOccupied(List<String> students, TimeSlot slot) {
        for (String studentId : students) {
            occupiedStudentTime.computeIfAbsent(studentId, k -> new HashSet<>()).add(slot);
        }
    }

    /**
     * 初始化已占用时间（从预处理数据中的已有占用记录）
     */
    private void initOccupiedTimes(SchedulingData data) {
        // 初始化教师已占用时间
        occupiedTeacherTime.putAll(data.getTeacherOccupancy());

        // 初始化教室已占用时间
        occupiedClassroomTime.putAll(data.getClassroomOccupancy());

        // 初始化学生已占用时间
        occupiedStudentTime.putAll(data.getStudentOccupancy());

        logger.info("初始化已占用时间完成 - 教师: {}, 教室: {}, 学生: {}",
                occupiedTeacherTime.size(),
                occupiedClassroomTime.size(),
                occupiedStudentTime.size());
    }

    /**
     * 重置排课状态
     */
    private void reset() {
        schedulingResult.clear();
        occupiedTeacherTime.clear();
        occupiedClassroomTime.clear();
        occupiedStudentTime.clear();
    }

    /**
     * 获取排课结果详情，包含完整信息
     */
    public List<String> getScheduleDetails() {
        List<String> details = new ArrayList<>();

        SchedulingData data = dataPreprocessor.getLatestSchedulingData();
        if (data == null) {
            details.add("暂无排课数据");
            return details;
        }

        for (Map.Entry<String, List<TimeSlot>> entry : schedulingResult.entrySet()) {
            String courseClassId = entry.getKey();
            CourseClassInfo course = data.getCourseClasses().get(courseClassId);
            if (course == null) {
                continue;
            }

            details.add(String.format("课程班: %s (%s), 每周 %d 次, 每次 %d 节, 周次: %d-%d周",
                    courseClassId, course.getClassName(),
                    course.getLessonCount(), course.getWeekType(),
                    course.getStartWeek(), course.getEndWeek()));

            for (TimeSlot slot : entry.getValue()) {
                details.add(String.format("  - %s", slot));
            }
            details.add("");
        }

        return details;
    }
}
